<?php
/**
 * coreModelCacheDriverFile
 *
 * 데이터 베이스 캐시 - 파일 드라이버
 *
 * 캐시 생성 경로
 * 루트/Uid/네임스페이스/해시(캐시명)
 *
 * @package core
 * @author  Platform Team
 */

class coreModelCacheDriverFile extends coreModelCacheDriverCommon
{
    /**
     * 캐시 루트 디렉토리
     */
    private $sRootDir;

    /**
     * 디렉토리 뎁스
     */
    private $iDirDepth;

    /**
     * 캐시 드라이버 구동
     *
     * @param array $aConfig
     */
    public function open($aConfig)
    {
        $this->sRootDir = $aConfig['DefaultPath'];
        $this->iDirDepth = $aConfig['DirectoryVariance'] > 3 ? 3 : $aConfig['DirectoryVariance'];
    }

    /**
     * 캐시 드라이버 종료
     */
    public function close()
    {
        //Empty
    }

    /**
     * 캐시 데이터 읽기
     *
     * @param string $sNsp
     * @param string $sKey
     * @return array(
     *  'identity' => 캐시 파일 경로 혹은 캐시 키(드라이버에 따라 다름)
     *  'itime' => 초기 생성 시간
     *  'expire' => 만료 시간
     *  'data' => 대상 데이터
     * ) | false
     */
    public function read($sNsp, $sKey)
    {
        $sNsp = $this->getNamespace($sNsp);
        $sDir = $this->_dir($sNsp);
        $sCacheName = $this->_cacheName($sKey);

        $sPath = $this->_createCacheFullPath($sDir, $sCacheName, true);

        if (!is_file($sPath)) return false;

        $sData = file_get_contents($sPath);

        $aGenerateData = unserialize($sData);

        if ($this->isValid($aGenerateData) === true) {
            $aGenerateData['identity'] = $sPath;
            return $aGenerateData;
        }

        return false;
    }

    /**
     * 캐시 데이터 생성
     *
     * @param string $sNsp
     * @param string $sKey
     * @param mixed $mValue
     * @param int $iExpire
     * @return boolean
     */
    public function write($sNsp, $sKey, $mValue, $iExpire)
    {
        if ($mValue === false) return false;

        $sNsp = $this->getNamespace($sNsp);
        $sDir = $this->_dir($sNsp);
        $sCacheName = $this->_cacheName($sKey);

        $sPath = $this->_createCacheFullPath($sDir, $sCacheName);

        $aGenerateData = $this->generateData($mValue, $iExpire);

        return file_put_contents($sPath, serialize($aGenerateData)) ? true : false;
    }

    /**
     * 캐시 데이터 만료
     *
     * @param string $sNsp
     * @return boolean
     */
    public function expire($sNsp)
    {
        $sNsp = $this->getNamespace($sNsp);
        $sDir = $this->_dir($sNsp);

        if (!is_dir($sDir)) {

            return false;

        }

        return $this->_removeDir($sDir);
    }

    /**
     * 루트 디렉토리 반환
     *
     * @param string $sNsp
     * @return string
     */
    private function _dir($sNsp)
    {
        $sDir = $this->sRootDir . '/' . $this->sUid . '/' . $sNsp;

        return $sDir;
    }

    /**
     * 캐시명 반환
     *
     * @param string $sKey
     * @return string
     */
    private function _cacheName($sKey)
    {
        $sName = md5($sKey);

        return $sName;
    }

    /**
     * 캐시 전체경로에 해당되는 디렉토리를 생성하고 전체 경로를 반환
     * (반환되는 값은 파일경로까지 포함입니다.)
     *
     * @param string $sDir          (디렉토리 경로)
     * @param string $sCacheName    (순수한 파일명)
     * @param boolean $isReadOnly    (디렉토리 생성을 진행하지 않습니다. 기본 : false)
     * @return string
     */
    private function _createCacheFullPath($sDir, $sCacheName, $isReadOnly=false)
    {
        $iDepth = $this->iDirDepth;

        if ($iDepth <= 0) {
            return $sDir . '/' . $sCacheName;
        }

        $iDepth = $iDepth > 3 ? 3 : $iDepth;

        for ($i=0; $i < $iDepth; ++$i) {
            $sDir .= '/' . $sCacheName[$i];
        }

        if ($isReadOnly === false && !is_dir($sDir)) {
            mkdir($sDir, 0777, true);
        }

        return $sDir . '/' . $sCacheName;
    }

    /**
     * 디렉토리 생성
     *
     * @param string $sDir
     */
    private function _createDir($sDir)
    {
        if (!is_dir($sDir)) {
            mkdir($sDir, 0777, true);
        }
    }

    /**
     * 디렉토리 삭제
     *
     * @param string $sDir
     * @param boolean $isSelf
     * @return boolean
     */
    private function _removeDir($sDir, $isSelf=true)
    {
        $result = true;

        $Opendir = opendir($sDir);

        while ( ($sName = readdir($Opendir)) !== false ) {

            if (strpos($sName, '.') === 0) continue;

            $sPath = $sDir.'/'.$sName;

            if (filetype($sPath) == 'dir') {
                $result = $this->_removeDir($sPath);
            } else if (filetype($sPath) == 'file') {
                $result = unlink($sPath);
            }


            if ($result === false) {
                break;
            }

        }

        closedir($Opendir);

        if ($isSelf === true) {
            rmdir($sDir);
        }

        return $result;
    }
}